package com.nio.demo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NIOServerDemo {

    private int port = 8081;

    //轮询器 selector
    private Selector selector;

    //缓冲区 buffer
    private ByteBuffer buffer = ByteBuffer.allocate(1024);

    public NIOServerDemo(int port){
        //初始化轮询器
        try {
            this.port = port;
            ServerSocketChannel server = ServerSocketChannel.open();

            //绑定地址
            //ip/port
            server.bind(new InetSocketAddress(this.port));
            //Nio是bio的升级 兼容 bio nio默认是堵塞的，
            server.configureBlocking(false);

            selector = Selector.open();

            server.register(selector, SelectionKey.OP_ACCEPT);

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public void listen(){
        System.out.println("listen on "+this.port +".");
        try {

            //轮询主线程
            while (true){
                //
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                
                
                //轮询迭代
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                //同步体现在这，每次只能处理一种状态
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    //每一个key代表一种状态
                    process(key);
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }

    }

    //具体处理业务
    //每一次轮询就是调用一次process方法，而每一次调用，只能干一件事
    private void process(SelectionKey key) throws IOException {
        //针对每一个状态给一个反应
        if(key.isAcceptable()){
            ServerSocketChannel server = (ServerSocketChannel)key.channel();

            //不管数据准没准备好，都要给反馈。
            SocketChannel channel = server.accept();
            //设置非堵塞
            channel.configureBlocking(false);
            //当准备就绪的时候，将状态改为可读
            key = channel.register(selector, SelectionKey.OP_READ);
        }else if(key.isReadable()){
            //key.channel() 从多路复用器中拿到客户端引用
            SocketChannel channel = (SocketChannel)key.channel();
            int len = channel.read(buffer);
            if (len>0) {
                buffer.flip();
                String content = new String(buffer.array(), 0, len);
                key = channel.register(selector, SelectionKey.OP_WRITE);
                //再key上携带信息，返回去
                key.attach(content);
                System.out.println("读取内容："+content);

            }
        }else if(key.isWritable()){
            SocketChannel channel = (SocketChannel) key.channel();
            String content = (String) key.attachment();
            channel.write(ByteBuffer.wrap(("输出："+content).getBytes()));
            channel.close();
        }
    }

    public static void main(String[] args) {
        new NIOServerDemo(8081).listen();

    }
}
